﻿using System.Collections.Generic;
using Microsoft.Xna.Framework;
using PenguinForce.Levels;
using PenguinForce.Entities;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Content;
using System;

namespace PenguinForce.Collisions
{
    public class CollisionManager
    {
        #region Constants

        //default value for the AI parameters
        private const float detectionDefault = 70.0f;
        private const float dangerDetectionDefault = float.MaxValue;
        private const float separationDefault = 100.0f;
        private const float moveInOldDirInfluenceDefault = 1.0f;
        private const float moveInFlockDirInfluenceDefault = 1.0f;
        private const float moveInRandomDirInfluenceDefault = 0.05f;
        private const float maxTurnRadiansDefault = 12.0f;
        private const float perMemberWeightDefault = 0.5f;
        private const float perDangerWeightDefault = 100.0f;

        #endregion

        #region Variables

        private Texture2D birdTex;
       
        private AIParameters flockParams;
        private Texture2D _waypointTexture;
        private ContentManager _content;

        public FootstepsController _stepController;

        #endregion

        #region Constructors

        static CollisionManager()
        {
            Instance = new CollisionManager();
        }

        private CollisionManager()
        {
            this.Entities = new List<ICollisionEntity>();
            this.Barriers = new List<BoundingBox>();
            this.ThinIce = new List<BoundingBox>();
            this.Holes = new List<BoundingBox>();

            this.Penguins = null;
            this.PolarBears = new List<PolarBear>();

            this.weakenedIce = new WeakenedIce(2);

            this.flockParams = new AIParameters();
            this.ResetAIParams();

            this._stepController = new FootstepsController();
        }

        #endregion

        #region Properties

        public static CollisionManager Instance { get; private set; }

        public List<ICollisionEntity> Entities { get; private set; }

        public List<BoundingBox> Barriers { get; private set; }

        public List<BoundingBox> Holes { get; private set; }

        public List<BoundingBox> ThinIce { get; private set; }

        public Flock Penguins { get; private set; }

        public List<PolarBear> PolarBears { get; private set; }

        private Grid CurrentLevel { get; set; }

        public Igloo Igloo { get; private set; }

        //public List<BoundingBox> weakenedIce { get; private set; }

        private WeakenedIce weakenedIce;

        private struct WeakenedIce
        {
            public List<BoundingBox> m_bbox;
            public List<int> counter;
            private int breaking_point;

            public WeakenedIce(int weight)
            {
                m_bbox = new List<BoundingBox>();
                counter = new List<int>();
                breaking_point = weight;
            }

            public void Clear()
            {
                m_bbox.Clear();
                counter.Clear();
            }

            public void Add(BoundingBox add)
            {
                if (m_bbox.Contains(add))
                {
                    int index = m_bbox.IndexOf(add);
                    counter[index]++;
                }
                else
                {
                    m_bbox.Add(add);
                    counter.Add(0);
                    
                    
                }

            }

            public List<BoundingBox> ReturnDestroyedIce()
            {

                List<BoundingBox> temp = new List<BoundingBox>(m_bbox);
                int i = 0;
                foreach (int count in counter)
                {
                    if (count < breaking_point)
                    {
                        m_bbox.RemoveAt(i);

                    }
                    else
                    {
                        i++;
                    }
                }

                return m_bbox;

            }

         

        }

        #endregion

        #region Methods

        /// <summary>
        /// Can "entity" occupy it's current position?
        /// </summary>
        /// <param name="entity"></param>
        /// <param name="position"></param>
        /// <returns></returns>
        public bool ContainsBarrier(ICollisionEntity entity)
        {
            if (!(new BoundingBox(Vector3.Zero, new Vector3(new Vector2(CurrentLevel.GetPixelWidth() - 1, CurrentLevel.GetPixelHeight() - 1), 0.0f))).Intersects(entity.Bounds))
            {
                return true;
            }
            foreach (BoundingBox barrier in this.Barriers)
            {
                if (entity.Bounds.Intersects(barrier))
                {
                    return true;
                }
            }
            return false;
        }

        public BoundingBox? ContainsThinIce(ICollisionEntity entity)
        {
            foreach (BoundingBox ice in this.ThinIce)
            {
                if (entity.Bounds.Intersects(ice))
                {
                    return ice;
                }
            }
            return null;
        }

        public bool ContainsHole(ICollisionEntity entity)
        {
            foreach (BoundingBox hole in this.Holes)
            {
                if (entity.Bounds.Intersects(hole))
                {
                    return true;
                }
            }
            return false;
        }

        public bool ContainsEntity(ICollisionEntity entity)
        {
            foreach (ICollisionEntity collisionEntity in this.Entities)
            {
                if (collisionEntity == this)
                {
                    continue;
                }
                if (collisionEntity.Bounds.Intersects(entity.Bounds))
                {
                    return true;
                }
            }
            return false;
        }

        public void InitializeLevel(ContentManager content)
        {
            this.Igloo = new Igloo(content.Load<Texture2D>("igloo"));

            birdTex = content.Load<Texture2D>("penguin");
            _waypointTexture = content.Load<Texture2D>("dot");

            _stepController.m_penguinFeetTexture = content.Load<Texture2D>("penguin-footprints");

            Texture2D nullTexture = content.Load<Texture2D>(@"blank");

            SpriteSheet tileTextureList = content.Load<SpriteSheet>("Tiles/TilesSprite");

            Texture2D startFlagTexture = content.Load<Texture2D>(@"startflag");
            Texture2D endFlagTexture = content.Load<Texture2D>(@"finishflag");

            this.CurrentLevel = new Grid(nullTexture, tileTextureList);
            Grid.m_startFlagTexture = startFlagTexture;
            Grid.m_finishFlagTexture = endFlagTexture;
            Grid.m_bearStartTexture = content.Load<Texture2D>("bear");

            _content = content;
        }

        public void LoadLevel(string levelName)
        {
            this.CurrentLevel = Grid.Load(levelName);

            this.Barriers.Clear();
            this.Entities.Clear();
            this.Holes.Clear();
            this.ThinIce.Clear();
            if (this.Penguins != null)
            {
                this.Penguins.Clear();
            }
            this.PolarBears.Clear();

            for (int row = 0; row < Grid.HEIGHT; row++)
            {
                for (int column = 0; column < Grid.WIDTH; column++)
                {
                    Tile tile = this.CurrentLevel.m_grid[column, row];


                    if (tile.m_tileType == Tile.TileTypes.Rock)
                    {


                        Vector2 min = new Vector2(column * Grid.GRID_SIZE, row * Grid.GRID_SIZE);
                        Vector2 max = min + new Vector2(Grid.GRID_SIZE - 1, Grid.GRID_SIZE - 1);

                        this.Barriers.Add(
                            new BoundingBox(
                                new Vector3(min, 0.0f),   // min value
                                new Vector3(max, 0.0f)    // max value
                            )
                        );
                    }
                    else if (tile.m_tileType == Tile.TileTypes.Hole)
                    {
                        Vector2 min = new Vector2(column * Grid.GRID_SIZE, row * Grid.GRID_SIZE);
                        Vector2 max = min + new Vector2(Grid.GRID_SIZE - 1, Grid.GRID_SIZE - 1);

                        this.Holes.Add(
                            new BoundingBox(
                                new Vector3(min, 0.0f),   // min value
                                new Vector3(max, 0.0f)    // max value
                            )
                        );
                    }
                    else if (tile.m_tileType == Tile.TileTypes.ThinIce)
                    {
                        Vector2 min = new Vector2(column * Grid.GRID_SIZE, row * Grid.GRID_SIZE);
                        Vector2 max = min + new Vector2(Grid.GRID_SIZE - 1, Grid.GRID_SIZE - 1);

                        this.ThinIce.Add(
                            new BoundingBox(
                                new Vector3(min, 0.0f),   // min value
                                new Vector3(max, 0.0f)    // max value
                            )
                        );
                    }
                }
            }

            SpawnFlock(this.CurrentLevel.m_startingPenguins);
            SpawnBears(_content);

            this.Igloo.Position = this.CurrentLevel.m_finishLocation;
        }

        public void ResetWeakenedIce()
        {
            weakenedIce.Clear();
        }

        public void BreakIce()
        {

            List<BoundingBox> destroyedIce = weakenedIce.ReturnDestroyedIce();
            foreach (BoundingBox box in destroyedIce)
            {
                CurrentLevel.m_grid[(int)(box.Min.X / CurrentLevel.GRIDSIZE), (int)(box.Min.Y / CurrentLevel.GRIDSIZE)].m_tileType = Tile.TileTypes.Hole;
                Holes.Add(box);
                ThinIce.Remove(box);

            }
        }
   

    

        public void WeakenIce(BoundingBox thinIceBox)
        {

            weakenedIce.Add(thinIceBox);

        }

        /// <summary>
        /// Create the bird flock
        /// </summary>
        /// <param name="theNum"></param>
        private void SpawnFlock(int flockSize)
        {
            Penguins = new Flock(flockSize, _content, _waypointTexture, flockParams);
        }

        private void SpawnBears(ContentManager content)
        {
            foreach (Vector2 position in this.CurrentLevel.m_bearStartList)
            {
                this.PolarBears.Add(new PolarBear(content, position));
            }
        }

        public Grid GetCurrentLevel()
        {
            return this.CurrentLevel;
        }

        /// <summary>
        /// Reset flock AI parameters
        /// </summary>
        private void ResetAIParams()
        {
            flockParams.detectionDist = detectionDefault;
            flockParams.dangerDetectionDist = dangerDetectionDefault;
            flockParams.separationDist = separationDefault;
            flockParams.moveInOldDirInfluence = moveInOldDirInfluenceDefault;
            flockParams.moveInFlockDirInfluence = moveInFlockDirInfluenceDefault;
            flockParams.moveInRandomDirInfluence = moveInRandomDirInfluenceDefault;
            flockParams.maxTurnRadians = maxTurnRadiansDefault;
            flockParams.perMemberWeight = perMemberWeightDefault;
            flockParams.perDangerWeight = perDangerWeightDefault;
        }

        public Penguin FindClosestPenguin(Vector2 position)
        {
            Penguin closestPenguin = null;
            float closestDistance = 0.0f;

            foreach (Penguin penguin in this.Penguins.flock)
            {
                float distance = Math.Abs(Vector2.Distance(position, penguin.Location));

                if (closestPenguin == null)
                {
                    closestPenguin = penguin;
                    closestDistance = distance;
                    continue;
                }

                if (distance < closestDistance)
                {
                    closestPenguin = penguin;
                    closestDistance = distance;
                }
            }

            return closestPenguin;
        }

        #endregion
    }
}
